Ваша задача — провести оценку результатов A/B-теста.
В вашем распоряжении есть датасет с действиями пользователей, техническое задание и несколько вспомогательных датасетов.
Оцените корректность проведения теста и проанализируйте его результаты.
Чтобы оценить корректность проведения теста:
удостоверьтесь, что нет пересечений с конкурирующим тестом и нет пользователей, участвующих в двух группах теста одновременно;
Техническое задание
Ожидаемый эффект: за 14 дней с момента регистрации в системе пользователи покажут улучшение каждой метрики не менее, чем на 5 процентных пунктов:
Загрузите данные теста, проверьте корректность его проведения и проанализируйте полученные результаты
Данные
data.csv
data.csv
data.csv
data.csv
Структура файлa
finish_dt — дата завершения кампании.
Структура файла:
device — устройство, с которого происходила регистрация.
Структура файла:
details — дополнительные данные о событии. Например, для покупок, purchase , в этом поле хранится стоимость покупки в долларах.
Структура файла:
План выполнения задания:
Оцените корректность проведения теста:
Выделите пользователей участвующих в тесте и проверьте:
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import matplotlib.pyplot as plt
from pandas.plotting import register_matplotlib_converters
import seaborn as sns
import plotly.express as px
from plotly import graph_objects as go
import datetime as dt
from datetime import timedelta
import warnings
import scipy.stats as stats
from scipy.stats import mannwhitneyu
import os
from statsmodels.stats.proportion import proportions_ztest
filename = "ab_project_marketing_events.csv"
filename1 = "final_ab_new_users.csv"
filename2 = "final_ab_events.csv"
filename3 = "final_ab_participants.csv"
downloads_folder = "C:\\Users\\123s\\Downloads\\"
df_ab = pd.read_csv(downloads_folder + filename)
df_user = pd.read_csv(downloads_folder + filename1)
df_event = pd.read_csv(downloads_folder + filename2)
df_participant = pd.read_csv(downloads_folder + filename3)
#df_ab = pd.read_csv('/datasets/ab_project_marketing_events.csv')
#df_user = pd.read_csv('/datasets/final_ab_new_users.csv')
#df_event = pd.read_csv('/datasets/final_ab_events.csv')
#df_participant = pd.read_csv('/datasets/final_ab_participants.csv')
def df_viewing(df):
display(df.head())
print('---------------------------------------------------------------------------------------------------------')
print(df.info())
print('---------------------------------------------------------------------------------------------------------')
print(df.describe())
print('---------------------------------------------------------------------------------------------------------')
print(df.isna().sum())
print('---------------------------------------------------------------------------------------------------------')
print(df.duplicated().sum())
df_viewing(df_ab)
| name | regions | start_dt | finish_dt | |
|---|---|---|---|---|
| 0 | Christmas&New Year Promo | EU, N.America | 2020-12-25 | 2021-01-03 |
| 1 | St. Valentine's Day Giveaway | EU, CIS, APAC, N.America | 2020-02-14 | 2020-02-16 |
| 2 | St. Patric's Day Promo | EU, N.America | 2020-03-17 | 2020-03-19 |
| 3 | Easter Promo | EU, CIS, APAC, N.America | 2020-04-12 | 2020-04-19 |
| 4 | 4th of July Promo | N.America | 2020-07-04 | 2020-07-11 |
---------------------------------------------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 14 entries, 0 to 13
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 name 14 non-null object
1 regions 14 non-null object
2 start_dt 14 non-null object
3 finish_dt 14 non-null object
dtypes: object(4)
memory usage: 576.0+ bytes
None
---------------------------------------------------------------------------------------------------------
name regions start_dt finish_dt
count 14 14 14 14
unique 14 6 14 14
top Christmas&New Year Promo APAC 2020-12-25 2021-01-03
freq 1 4 1 1
---------------------------------------------------------------------------------------------------------
name 0
regions 0
start_dt 0
finish_dt 0
dtype: int64
---------------------------------------------------------------------------------------------------------
0
Датасет ,df_ab- календарь маркетинговых событий на 2020 год. Содержит 14 маркетинговых событий, в 6 регионах. Пропусков и явных дубликатов нет. Требуется преобразование столбов с датой в datetime и смнена названия столбца с name, regions
df_ab.columns = ['event_name','region','start_dt','finish_dt']
df_ab['start_dt'] = pd.to_datetime(df_ab['start_dt'])
df_ab['finish_dt'] = pd.to_datetime(df_ab['finish_dt'])
df_ab.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 14 entries, 0 to 13 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 event_name 14 non-null object 1 region 14 non-null object 2 start_dt 14 non-null datetime64[ns] 3 finish_dt 14 non-null datetime64[ns] dtypes: datetime64[ns](2), object(2) memory usage: 576.0+ bytes
df_viewing(df_user)
| user_id | first_date | region | device | |
|---|---|---|---|---|
| 0 | D72A72121175D8BE | 2020-12-07 | EU | PC |
| 1 | F1C668619DFE6E65 | 2020-12-07 | N.America | Android |
| 2 | 2E1BF1D4C37EA01F | 2020-12-07 | EU | PC |
| 3 | 50734A22C0C63768 | 2020-12-07 | EU | iPhone |
| 4 | E1BDDCE0DAFA2679 | 2020-12-07 | N.America | iPhone |
---------------------------------------------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 61733 entries, 0 to 61732
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 user_id 61733 non-null object
1 first_date 61733 non-null object
2 region 61733 non-null object
3 device 61733 non-null object
dtypes: object(4)
memory usage: 1.9+ MB
None
---------------------------------------------------------------------------------------------------------
user_id first_date region device
count 61733 61733 61733 61733
unique 61733 17 4 4
top D72A72121175D8BE 2020-12-21 EU Android
freq 1 6290 46270 27520
---------------------------------------------------------------------------------------------------------
user_id 0
first_date 0
region 0
device 0
dtype: int64
---------------------------------------------------------------------------------------------------------
0
В df_user-все пользователи, зарегистрировавшиеся в интернет-магазине в период с 7 по 21 декабря 2020 года; 61713 , требуется преобразовать столбец со временем
df_user['first_date'] = pd.to_datetime(df_user['first_date'])
df_user.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 61733 entries, 0 to 61732 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 user_id 61733 non-null object 1 first_date 61733 non-null datetime64[ns] 2 region 61733 non-null object 3 device 61733 non-null object dtypes: datetime64[ns](1), object(3) memory usage: 1.9+ MB
df_viewing(df_event)
| user_id | event_dt | event_name | details | |
|---|---|---|---|---|
| 0 | E1BDDCE0DAFA2679 | 2020-12-07 20:22:03 | purchase | 99.99 |
| 1 | 7B6452F081F49504 | 2020-12-07 09:22:53 | purchase | 9.99 |
| 2 | 9CD9F34546DF254C | 2020-12-07 12:59:29 | purchase | 4.99 |
| 3 | 96F27A054B191457 | 2020-12-07 04:02:40 | purchase | 4.99 |
| 4 | 1FD7660FDF94CA1F | 2020-12-07 10:15:09 | purchase | 4.99 |
---------------------------------------------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 440317 entries, 0 to 440316
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 user_id 440317 non-null object
1 event_dt 440317 non-null object
2 event_name 440317 non-null object
3 details 62740 non-null float64
dtypes: float64(1), object(3)
memory usage: 13.4+ MB
None
---------------------------------------------------------------------------------------------------------
details
count 62740.000000
mean 23.877631
std 72.180465
min 4.990000
25% 4.990000
50% 4.990000
75% 9.990000
max 499.990000
---------------------------------------------------------------------------------------------------------
user_id 0
event_dt 0
event_name 0
details 377577
dtype: int64
---------------------------------------------------------------------------------------------------------
0
В df_event-все события новых пользователей в период с 7 декабря 2020 по 4 января 2021 года; Меняю тип данных в столбце время и изучаю 70% пропусков в стобце details. Добавлю столбец с датой
df_event['event_dt'] = pd.to_datetime(df_event['event_dt'])
df_event['date'] = df_event['event_dt'].dt.date
df_event['date'] = pd.to_datetime(df_event['date'], format='%Y-%m-%d')
df_event.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 440317 entries, 0 to 440316 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 user_id 440317 non-null object 1 event_dt 440317 non-null datetime64[ns] 2 event_name 440317 non-null object 3 details 62740 non-null float64 4 date 440317 non-null datetime64[ns] dtypes: datetime64[ns](2), float64(1), object(2) memory usage: 16.8+ MB
df_event['event_name'].unique()
array(['purchase', 'product_cart', 'product_page', 'login'], dtype=object)
df_event.tail()
| user_id | event_dt | event_name | details | date | |
|---|---|---|---|---|---|
| 440312 | 245E85F65C358E08 | 2020-12-30 19:35:55 | login | NaN | 2020-12-30 |
| 440313 | 9385A108F5A0A7A7 | 2020-12-30 10:54:15 | login | NaN | 2020-12-30 |
| 440314 | DB650B7559AC6EAC | 2020-12-30 10:59:09 | login | NaN | 2020-12-30 |
| 440315 | F80C9BDDEA02E53C | 2020-12-30 09:53:39 | login | NaN | 2020-12-30 |
| 440316 | 7AEC61159B672CC5 | 2020-12-30 11:36:13 | login | NaN | 2020-12-30 |
df_event.groupby('event_name')['details'].count()
event_name login 0 product_cart 0 product_page 0 purchase 62740 Name: details, dtype: int64
Пропуски во всех событиях кроме purchase, востановить не возможно
df_viewing(df_participant)
| user_id | group | ab_test | |
|---|---|---|---|
| 0 | D1ABA3E2887B6A73 | A | recommender_system_test |
| 1 | A7A3664BD6242119 | A | recommender_system_test |
| 2 | DABC14FDDFADD29E | A | recommender_system_test |
| 3 | 04988C5DF189632E | A | recommender_system_test |
| 4 | 482F14783456D21B | B | recommender_system_test |
---------------------------------------------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 18268 entries, 0 to 18267
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 user_id 18268 non-null object
1 group 18268 non-null object
2 ab_test 18268 non-null object
dtypes: object(3)
memory usage: 428.3+ KB
None
---------------------------------------------------------------------------------------------------------
user_id group ab_test
count 18268 18268 18268
unique 16666 2 2
top 0FDFDA0B2DEC2D91 A interface_eu_test
freq 2 9655 11567
---------------------------------------------------------------------------------------------------------
user_id 0
group 0
ab_test 0
dtype: int64
---------------------------------------------------------------------------------------------------------
0
В df_participant-таблица участников тестов. Пропусков, дубликатов нет. Уникальных пользователей меньше, чем строк в этом датасете, значит некоторые пользовател вошли в разные группы/тесты
df_participant['ab_test'].unique()
array(['recommender_system_test', 'interface_eu_test'], dtype=object)
df_participant['group'].unique()
array(['A', 'B'], dtype=object)
df_participant.groupby('group')['ab_test'].count()
group A 9655 B 8613 Name: ab_test, dtype: int64
t = df_participant.query('ab_test=="recommender_system_test"')
t.groupby('group')['ab_test'].count()
group A 3824 B 2877 Name: ab_test, dtype: int64
df_user_t = t['user_id'].nunique()
users_both_groups = df_participant.groupby('user_id').filter(lambda x: x['group'].nunique() == 2)
users_both_groups.head()
| user_id | group | ab_test | |
|---|---|---|---|
| 25 | EAFB9027A27D510C | B | recommender_system_test |
| 34 | C758C53D624AD1AC | B | recommender_system_test |
| 35 | 8C478EE1AE7C98A4 | B | recommender_system_test |
| 44 | FC3F3E4DA7C85F88 | A | recommender_system_test |
| 49 | FE2AF0E94DBD470E | A | recommender_system_test |
users_both_count = users_both_groups['user_id'].count()
Итого 1552 пользователя входят одновременно и в группу А и В
df_participant.query('user_id=="EAFB9027A27D510C"')
| user_id | group | ab_test | |
|---|---|---|---|
| 25 | EAFB9027A27D510C | B | recommender_system_test |
| 11652 | EAFB9027A27D510C | A | interface_eu_test |
df_participant.query('user_id=="CE782A3646E8E5E1"')
| user_id | group | ab_test | |
|---|---|---|---|
| 1744 | CE782A3646E8E5E1 | B | recommender_system_test |
| 18258 | CE782A3646E8E5E1 | A | interface_eu_test |
Посмотрим для нашего теста
users_both_recommender = t.groupby('user_id').filter(lambda x: x['group'].nunique() == 2)
users_both_recommender
| user_id | group | ab_test |
|---|
Проверим процент попавших и в наш тест и в соседний
procent_user = round(users_both_count/df_user_t*100,1)
print(f"Процент пользователей попавших в обе группы от общего количества: {procent_user}%")
Процент пользователей попавших в обе группы от общего количества: 23.2%
В нашем тесте(recommender_system_test) таких нет. То есть пользователь входит и в разные группы но и в разные тесты. И таких 23,2% от общего числа, участников теста. Удалить такое количество не получиться
Так как на контрольную группу А воздействия не оказывается, то пересечение не влияет на результат теста, проверим пересечение только в группе В
t_group_b = df_participant.query('group=="B"')
users_both_t_group_b = t_group_b.groupby('user_id').filter(lambda x: x['ab_test'].nunique() == 2)
users_both_t_group_b
| user_id | group | ab_test | |
|---|---|---|---|
| 29 | 5D5E6EE92AF6E9E0 | B | recommender_system_test |
| 53 | 952D1EEBF552BC95 | B | recommender_system_test |
| 79 | B3C1FF8D21EAC16B | B | recommender_system_test |
| 105 | 04BE4EFE4C457312 | B | recommender_system_test |
| 124 | A0794039D643F1B6 | B | recommender_system_test |
| ... | ... | ... | ... |
| 18079 | 8FF8F87305BB9A7D | B | interface_eu_test |
| 18094 | C2025648F77BD80B | B | interface_eu_test |
| 18170 | 7DF21AEB1AA231F9 | B | interface_eu_test |
| 18183 | EA6EA431FF84563B | B | interface_eu_test |
| 18245 | 2B0CD24EE4291CA0 | B | interface_eu_test |
688 rows × 3 columns
t_group_b.query('user_id=="5D5E6EE92AF6E9E0"')
| user_id | group | ab_test | |
|---|---|---|---|
| 29 | 5D5E6EE92AF6E9E0 | B | recommender_system_test |
| 14113 | 5D5E6EE92AF6E9E0 | B | interface_eu_test |
t_group_b.query('user_id=="7DF21AEB1AA231F9"')
| user_id | group | ab_test | |
|---|---|---|---|
| 3868 | 7DF21AEB1AA231F9 | B | recommender_system_test |
| 18170 | 7DF21AEB1AA231F9 | B | interface_eu_test |
Пересечение пользователей по тестовой группе 688 пользователей.
user_t_b = users_both_t_group_b['user_id'].nunique()
df_partition_b = df_participant.query('group=="B"')
df_user_b = df_partition_b['user_id'].nunique()
proc_user_b = round(user_t_b/df_user_b*100,2)
print(f"Процент пользователей группы В от общего числа пользователей группы В обоих тестов: {proc_user_b}%")
Процент пользователей группы В от общего числа пользователей группы В обоих тестов: 4.16%
t_group_a = df_participant.query('group=="A"')
users_both_t_group_a = t_group_a.groupby('user_id').filter(lambda x: x['ab_test'].nunique() == 2)
user_t_a = users_both_t_group_a['user_id'].nunique()
df_partition_a = df_participant.query('group=="A"')
df_user_a = df_partition_a['user_id'].nunique()
proc_user_a = round(user_t_a/df_user_a*100,2)
print(f"Процент пользователей группы A от общего числа пользователей группы В обоих тестов: {proc_user_a}%")
Процент пользователей группы A от общего числа пользователей группы В обоих тестов: 5.25%
Процент попавших в обе группы достаточно равномерный, поэтому и воздействие на группы оказывает равномерное
Пересечение с конкурирующим тестом, без разбивки на группы
users_both_test = df_participant.groupby('user_id').filter(lambda x: x['ab_test'].nunique() == 2)
len(users_both_test)
3204
arew_user = df_participant.query('ab_test == "recommender_system_test"')['user_id'].unique()
unis_user = df_participant.query('ab_test == "interface_eu_test"')['user_id'].unique()
common_users = np.intersect1d(arew_user, unis_user)
len(common_users)
1602
1602 Пользователя одновременно входят и в наш тест и в конкурирующий
внутри нашего теста распределение пользователей из второй тестовой группы между группами нашего теста
arew_common = df_participant[(df_participant['user_id'].isin(common_users)) & (df_participant['ab_test'] == 'recommender_system_test')]
group_counts = arew_common.groupby('group')['user_id'].nunique()
group_counts
group A 921 B 681 Name: user_id, dtype: int64
arew_common_a = arew_common.query('group=="A"')['user_id'].nunique()
arew_common_b = arew_common.query('group=="B"')['user_id'].nunique()
df_rec_test_aa = df_participant.query('ab_test=="recommender_system_test" & group=="A"')['user_id'].nunique()
df_rec_test_bb = df_participant.query('ab_test=="recommender_system_test" & group=="B"')['user_id'].nunique()
proc_a_rec = round(arew_common_a/df_rec_test_aa*100,2)
print(f"Процент пользователей группы А от общего числа пользователей группы А нашего теста: {proc_a_rec}%")
Процент пользователей группы А от общего числа пользователей группы А нашего теста: 24.08%
proc_user_b_rec = round(arew_common_b/df_rec_test_bb*100,2)
print(f"Процент пользователей группы В от общего числа пользователей группы В нашего теста: {proc_user_b_rec}%")
Процент пользователей группы В от общего числа пользователей группы В нашего теста: 23.67%
Около 24% пользователей из группы А конкурирующего теста входят в группу А нашего теста, и 23.5% по группе В
df_ab_filt = df_ab[df_ab['region'].str.contains('EU')]
df_ab_filt = df_ab_filt.query('start_dt>="2020-12-07" and finish_dt<="2021-01-04"')
df_ab_filt
| event_name | region | start_dt | finish_dt | |
|---|---|---|---|---|
| 0 | Christmas&New Year Promo | EU, N.America | 2020-12-25 | 2021-01-03 |
Только одно мероприятие проходило в период и регионе проведения нашего теста. С 25/12 по 03/01
print(df_user['first_date'].min())
print(df_user['first_date'].max())
2020-12-07 00:00:00 2020-12-23 00:00:00
Фильтрую новых пользователей про региону и дате
df_user['region'].unique()
array(['EU', 'N.America', 'APAC', 'CIS'], dtype=object)
df_user_f = df_user.query('region=="EU" and "2020-12-07"<=first_date<="2020-12-21"').reset_index()
df_user_f.head()
| index | user_id | first_date | region | device | |
|---|---|---|---|---|---|
| 0 | 0 | D72A72121175D8BE | 2020-12-07 | EU | PC |
| 1 | 2 | 2E1BF1D4C37EA01F | 2020-12-07 | EU | PC |
| 2 | 3 | 50734A22C0C63768 | 2020-12-07 | EU | iPhone |
| 3 | 7 | 8942E64218C9A1ED | 2020-12-07 | EU | PC |
| 4 | 9 | FFCEA1179C253104 | 2020-12-07 | EU | Android |
print(df_user_f['first_date'].min())
print(df_user_f['first_date'].max())
print(df_user_f['user_id'].nunique())
2020-12-07 00:00:00 2020-12-21 00:00:00 42340
Отфильтровал пользователей по дате и региону, осталось 42340 новых пользователей. Данные по пользователям соответствую нашему ТЗ набирались с 07-12 по 21-12
Фильтрую события
df_event.head()
| user_id | event_dt | event_name | details | date | |
|---|---|---|---|---|---|
| 0 | E1BDDCE0DAFA2679 | 2020-12-07 20:22:03 | purchase | 99.99 | 2020-12-07 |
| 1 | 7B6452F081F49504 | 2020-12-07 09:22:53 | purchase | 9.99 | 2020-12-07 |
| 2 | 9CD9F34546DF254C | 2020-12-07 12:59:29 | purchase | 4.99 | 2020-12-07 |
| 3 | 96F27A054B191457 | 2020-12-07 04:02:40 | purchase | 4.99 | 2020-12-07 |
| 4 | 1FD7660FDF94CA1F | 2020-12-07 10:15:09 | purchase | 4.99 | 2020-12-07 |
print(df_event['date'].min())
print(df_event['date'].max())
print(df_event['user_id'].nunique())
2020-12-07 00:00:00 2020-12-30 00:00:00 58703
Данные в тесте только с 07-12 по 30-12, учитывая что наш тест продолжался до 04-01 то данных за 5 дней не достает. Востановить данные не возможно. Пользователей 58703. Обьединяю таблицы с событиями и новыми пользователями, тогда отсечем всех старых кто есть в датасете события и из датасета новые юзеры всех кто не совершал ни одного события
df_user_event = df_user_f.merge(df_event, on='user_id', how='left').drop('index', axis=1)
df_user_event.head()
| user_id | first_date | region | device | event_dt | event_name | details | date | |
|---|---|---|---|---|---|---|---|---|
| 0 | D72A72121175D8BE | 2020-12-07 | EU | PC | 2020-12-07 21:52:10 | product_page | NaN | 2020-12-07 |
| 1 | D72A72121175D8BE | 2020-12-07 | EU | PC | 2020-12-07 21:52:07 | login | NaN | 2020-12-07 |
| 2 | 2E1BF1D4C37EA01F | 2020-12-07 | EU | PC | 2020-12-07 09:05:47 | product_cart | NaN | 2020-12-07 |
| 3 | 2E1BF1D4C37EA01F | 2020-12-07 | EU | PC | 2020-12-10 04:13:53 | product_cart | NaN | 2020-12-10 |
| 4 | 2E1BF1D4C37EA01F | 2020-12-07 | EU | PC | 2020-12-12 17:54:57 | product_cart | NaN | 2020-12-12 |
df_user_event['user_id'].nunique()
42340
Создали таблицу с новыми пользователями и событиями которые он совершили, исходя из тех. задания. Таблицу с участниками нашего теста мы проверяли ранее. Наш тест recommender_system_test есть в этой таблице и также есть наши группы А и В
df_user_event_t = df_user_event.merge(t, on='user_id', how='left')
df_user_event_t.head()
| user_id | first_date | region | device | event_dt | event_name | details | date | group | ab_test | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | D72A72121175D8BE | 2020-12-07 | EU | PC | 2020-12-07 21:52:10 | product_page | NaN | 2020-12-07 | A | recommender_system_test |
| 1 | D72A72121175D8BE | 2020-12-07 | EU | PC | 2020-12-07 21:52:07 | login | NaN | 2020-12-07 | A | recommender_system_test |
| 2 | 2E1BF1D4C37EA01F | 2020-12-07 | EU | PC | 2020-12-07 09:05:47 | product_cart | NaN | 2020-12-07 | NaN | NaN |
| 3 | 2E1BF1D4C37EA01F | 2020-12-07 | EU | PC | 2020-12-10 04:13:53 | product_cart | NaN | 2020-12-10 | NaN | NaN |
| 4 | 2E1BF1D4C37EA01F | 2020-12-07 | EU | PC | 2020-12-12 17:54:57 | product_cart | NaN | 2020-12-12 | NaN | NaN |
df = df_user_event.merge(t, on='user_id', how='left')
df_new = t.merge(df_user_event, on='user_id', how='inner')
df_new.head()
| user_id | group | ab_test | first_date | region | device | event_dt | event_name | details | date | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | D1ABA3E2887B6A73 | A | recommender_system_test | 2020-12-07 | EU | PC | 2020-12-07 14:43:27 | purchase | 99.99 | 2020-12-07 |
| 1 | D1ABA3E2887B6A73 | A | recommender_system_test | 2020-12-07 | EU | PC | 2020-12-25 00:04:56 | purchase | 4.99 | 2020-12-25 |
| 2 | D1ABA3E2887B6A73 | A | recommender_system_test | 2020-12-07 | EU | PC | 2020-12-07 14:43:29 | product_cart | NaN | 2020-12-07 |
| 3 | D1ABA3E2887B6A73 | A | recommender_system_test | 2020-12-07 | EU | PC | 2020-12-25 00:04:57 | product_cart | NaN | 2020-12-25 |
| 4 | D1ABA3E2887B6A73 | A | recommender_system_test | 2020-12-07 | EU | PC | 2020-12-07 14:43:27 | product_page | NaN | 2020-12-07 |
df_user_unique_t = df_user_event_t['user_id'].nunique()
df_user_event_t['ab_test'].unique()
array(['recommender_system_test', nan], dtype=object)
df_user_event_t.isna().sum()
user_id 0 first_date 0 region 0 device 0 event_dt 2874 event_name 2874 details 260609 date 2874 group 279237 ab_test 279237 dtype: int64
Ожидаемое количество участникв теста
filtered_df_t = df_new['user_id'].nunique()
nan_percent = round(filtered_df_t/df_user_unique_t*100,1)
print(f"Уникальных пользователей: {filtered_df_t}")
print(f"Процент пользователей участвовавших в нашем эксперименте 'test': {nan_percent}%")
Уникальных пользователей: 6351 Процент пользователей участвовавших в нашем эксперименте 'test': 15.0%
Только около 15.0 % новых пользователей участвовало в нашем тесте, от общего числа зарегестрировынных в период проведения теста и регион EU
Ожидаемый эффект: за 14 дней с момента регистрации в системе пользователи покажут улучшение каждой метрики не менее, чем на 5 процентных пунктов:
(Так как данные по 30-12 только, то мы ищем возраст события, из обьединеной таблицы с участника тестов из Европы и мы можем для каждого события посчитать возраст, есть регистрации пользователей и день совершения события, из дня совершения события вычитаем день регистрации и получаем тот день на которое было совершено событие, те 0 значит событие = регистрации. Момент, не все пользоватли совершали события, нужно проанализировать сколько их из группы А и В. После этого фильтруем наш ДС с возрастом <=13 дней. Учесть NaN, и получить отфильтроынный датасет. И проверить нужныли были 14 дней, строим гистограмма х возраст и у количество событий двойная для обоих групп.)
df_ab_group = df_user_event_t.query('ab_test=="recommender_system_test"')
df_ab_group.groupby('group')['user_id'].count()
group A 19339 B 6951 Name: user_id, dtype: int64
df_ab_group = df_ab_group.reset_index(drop=True)
df_ab_group['day_count'] = df_ab_group['date'] - df_ab_group['first_date']
df_ab_group['day_count'] = df_ab_group['day_count'].apply(lambda x: x.days)
df_ab_group['day_count'].value_counts()
0.0 7719 1.0 3559 2.0 2451 3.0 1712 4.0 1417 5.0 1149 6.0 999 7.0 924 8.0 725 9.0 578 10.0 481 12.0 341 11.0 322 13.0 243 14.0 208 15.0 163 16.0 94 17.0 78 18.0 77 20.0 62 19.0 54 21.0 31 22.0 29 23.0 4 Name: day_count, dtype: int64
df_ab_group.head()
| user_id | first_date | region | device | event_dt | event_name | details | date | group | ab_test | day_count | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | D72A72121175D8BE | 2020-12-07 | EU | PC | 2020-12-07 21:52:10 | product_page | NaN | 2020-12-07 | A | recommender_system_test | 0.0 |
| 1 | D72A72121175D8BE | 2020-12-07 | EU | PC | 2020-12-07 21:52:07 | login | NaN | 2020-12-07 | A | recommender_system_test | 0.0 |
| 2 | E6DE857AFBDC6102 | 2020-12-07 | EU | PC | NaT | NaN | NaN | NaT | B | recommender_system_test | NaN |
| 3 | DD4352CDCF8C3D57 | 2020-12-07 | EU | Android | 2020-12-07 15:32:54 | product_page | NaN | 2020-12-07 | B | recommender_system_test | 0.0 |
| 4 | DD4352CDCF8C3D57 | 2020-12-07 | EU | Android | 2020-12-08 08:29:31 | product_page | NaN | 2020-12-08 | B | recommender_system_test | 1.0 |
df_ab_group.groupby(['user_id','event_name'])['day_count'].count()
user_id event_name
001064FEAAB631A1 login 3
product_page 3
0010A1C096941592 login 4
product_page 4
purchase 4
..
FFAE9489C76F352B login 3
product_page 3
FFF28D02B1EACBE1 login 3
product_cart 3
product_page 3
Name: day_count, Length: 7767, dtype: int64
df_ab_group.groupby('user_id')['event_name'].count()
user_id
000ABE35EE11412F 0
001064FEAAB631A1 6
0010A1C096941592 12
001C05E87D336C59 0
00341D8401F0F665 2
..
FFC2C5F898D1245B 0
FFC53FD45DDA5EE8 0
FFE858A7845F005E 0
FFED90241D04503F 0
FFF28D02B1EACBE1 9
Name: event_name, Length: 6351, dtype: int64
def group_and_ungroup(x):
return x.day_count.count(), np.sum(x.day_count)
grouped = df_ab_group.groupby(['user_id', 'event_name']).apply(group_and_ungroup)
grouped
user_id event_name
001064FEAAB631A1 login (3, 7.0)
product_page (3, 7.0)
0010A1C096941592 login (4, 12.0)
product_page (4, 12.0)
purchase (4, 12.0)
...
FFAE9489C76F352B login (3, 8.0)
product_page (3, 8.0)
FFF28D02B1EACBE1 login (3, 13.0)
product_cart (3, 13.0)
product_page (3, 13.0)
Length: 7767, dtype: object
user_counts = df_ab_group.groupby('user_id')['event_name'].nunique()
single_event_users = user_counts[user_counts == 1].index.tolist()
print(single_event_users)
['00341D8401F0F665', '00505E15A9D81546', '00A52DCF85F1BE03', '016F758EB5C5A5DA', '01BD731FB5E57AE6', '0205944A5D3DC004', '0225F4E8E0D98BA4', '0240DB73D9B6F136', '0386BA6137787A24', '040DB681B8CFAACA', '04BE4EFE4C457312', '04F2CF340B4F3822', '051D59BC38C3B3AA', '05CB67D01740EF6E', '0657ECDBA63E5FD6', '06C9AD50396A67F2', '08DF1B2539351AAB', '09BD5F7225CD0F7E', '0A5E3BE3C51CB47A', '0B08BA67D85527C5', '0C2FF828F063B7AD', '0CA61E320CBC8D4F', '0CA93D3A4A1D8389', '0E77217AC0A85CB2', '0EBE4BDE16D19DC0', '0F1349E8B6D23E43', '0F6573B9431935D2', '0F7D49FC184EDCDE', '0FBF679EA1340786', '0FC3C8BC1348A405', '0FF10274AC68852A', '10018FAD7C1BEB3E', '102613D89170C020', '10387B8D694D539C', '1042C7577F706938', '104E2E7457341647', '10C645F9BDAD5E5B', '10EA2E44272E32A8', '111E0B8A76E90532', '11970FE3A608F9EF', '122C0E3A8F27AC93', '127DA1103176E88E', '12C0F58AEECAEA2F', '12C4599F88ABEC86', '12FCEFC7D1907D47', '135F6688A1DC637B', '1381632DA8AD52C0', '139519F1ADB40E0D', '13E1E6999FEA8C82', '14274CC0FEE00801', '1484BBF124DB1B18', '14AEC962812EB706', '154228E9B2DA7F16', '1544489D5476AD59', '15F57595AB51A15C', '1671B60B525AD59C', '16B827FC823C52A8', '16E9BCCA73DF4AF5', '16F2DB656CC2F952', '172F0C1F993BE914', '173B960BB44D4E22', '181181D6E7BA82B4', '181DA774FA96EDB6', '1839CDFE4E9E10B3', '18D0F694EA816E7D', '196E3C3541EB8BE1', '1AA90C1AD5727610', '1BC8D784255D5E67', '1C5874DE0D952A0D', '1C711C65356BCFBF', '1D7745C3BC072443', '1D95C80183156D87', '1E7B931452B2F965', '1E7CED0F8870180C', '1E8329A6915FB27E', '1EB1C8E41C1DD5EE', '1F2C38ADEF57670B', '1F8FAE6372EB9C6C', '20155135A98368DA', '21C2D41E1FDF1E70', '21E6AD81A122B126', '22092C653019E91C', '239838E03D995168', '23DDD27AC3FEFA63', '23F551153080EBAB', '249230DC3628A7AE', '24D35A2E59D68D66', '24D3AAA91EFD597F', '2599F6408D61FAD1', '25B6EE0F91D32DB9', '2626A4773033458D', '2686619F5A2AE06C', '2694629861CC9154', '26AB15B163D012E4', '26B0521FC2BAB2D1', '27796B536E6C0F6D', '2784913F3447A9EE', '2853F21A0E52F7AA', '28D889F742D7CB1F', '28F165B39D160BC5', '2901C103BDFD8B58', '2905EDBC782A9E69', '2A114ED4FE2F5FED', '2A12393A2BBFF701', '2A7220F590E2EB8E', '2A7E32FA8EF4DBA1', '2B68D50DAADB1DC4', '2C5E1D23A27078A0', '2C8D5D38742CD38D', '2CBFC548B33C0259', '2CC15FB4F021D006', '2CDFC246EAEEAB13', '2CEC84A86958E639', '2D53E02A0E04DC84', '2E53814463A4BE74', '2EBA55084462645A', '2ED365A23D725DB0', '2F9EC4B16D68E2F1', '30D6A716BEDA75C8', '312C37647F3D5433', '315CB3A5EA257B8E', '31D4A79415209872', '32244E379E754915', '3243339CAFFA806B', '327EB6851FEAD2E9', '33CEAE52626CA99E', '340D9C518D3CBB8D', '34BBBAC1CCC0F4F9', '35795D0E78C18AE4', '35EF0E3272713CFC', '3656B8F3DEF02DA0', '375CA26C16C90078', '38B78992508A9283', '38BCEA4860D38AA9', '38DA0BA08A977520', '39E330A497886B80', '3A7987AFAA3A5F47', '3AE5E3EE2C8A8727', '3B2470E45416C59A', '3B3AE26404B64EF2', '3B3F68DC5779E517', '3B4FF434FA8193B6', '3C02D15BA7E8CD69', '3C7868D5FEDB39F7', '3D264F3D953DBA67', '3D8509A61CEED2B2', '3D960D370AF76942', '3E2016ED6EDB5841', '3ED90BE0DC2A3FD3', '3F52D53AB33C9FD6', '3FAA6AD64C3B47E8', '4089CD204802F5F8', '40C11E04E846C139', '4122B8F30EB2271C', '41720B0DC37FACD5', '4266741E592070B6', '427C5593BE58CA0A', '42ECCBA921AFE086', '42FC1295136570BA', '436D6F820AE149EA', '43D07273336C97BC', '4400B2091A6E2CCD', '44037F187E376CD3', '449330634C4DD2EA', '44BD1495B3FB208B', '4566C623449592A7', '45D971502E89E65E', '463D4D4948E0D4A4', '465BEC2A7190853B', '46BDEF99D733A7B7', '46C03A54CD7F3F6E', '479E6D6A1693C32E', '47A0EAE52F635E59', '47A4DAD2DBC2DDA0', '47ADBF580CF107E1', '47FD6B1D75C29C53', '48526B8C7F8459FD', '489B8E34D1E65CCA', '48FEAA2BDBC721D4', '4949578BF1954CF5', '49C52F2638D25D2F', '4A53CB855B71A848', '4A61A28F35795FD1', '4A9D5A8F91CA5C62', '4B479E626904CF59', '4B60670F850D5024', '4B6BA21C73F66940', '4C30B83191F4E868', '4CAED71575E5EA2F', '4CCFDC7BE9B6FE73', '4CF8E8855FB1A493', '4CFBECEF398176DA', '4D921165CD2825D1', '4DE200C2E6892148', '4E43940B0CF8D947', '4E5C9F61B66394F7', '4EB733A6B2C93533', '4ECCD93E1F247611', '4F556AAB06E086E5', '4FFEF3C8471FF8EA', '505D67B4B604CF79', '50CF32FEE0C61DA9', '51560FF11CCA1F74', '51CFE2034A743204', '51DA2C882D0D7508', '5207E56E697027E9', '52828376E649CA27', '52A0547B1B31907F', '52E7B7DC39F52987', '535DC74493925CF8', '53638472C31218BA', '53732758D90FB1E4', '5426EFD4BBAEA349', '542DD6DB81108686', '54E564A7253C7084', '552AF033F097DE56', '55978D947D078E6A', '55F1DDF55DD42893', '564975AFF8E15C0C', '565EF59DDD36B95B', '57150D3838DE301A', '5732FDB1C948FE26', '57442DD4178AFE27', '576F0756184C8DB1', '583CC6A8A1A8F809', '58D06B81851522E6', '58F29FD5F5BEEF1E', '59322F3BAE5D0668', '597F7FD5AA3524D3', '59FF17724B8BCE48', '5A3B4228AD33781F', '5A3CFCB7782BB05F', '5AD2A85FFA7D7EBE', '5B05449E69A2AD9E', '5B0EB7BAB456C0FE', '5B4EFE916AD19741', '5B5F8713DF915802', '5B671D912F9D48BE', '5BDF2A772826F42C', '5C909C0F25ACD34E', '5C9E46262BBA9A24', '5D93D429975AAA1D', '5E4F77BDD1723F58', '5E6B0B086089C4CF', '5E9E80BADEA372AB', '5F09B85B875AA41D', '5F89649C6337CE7D', '5FE8A7964BFE9201', '5FED9C8EB157278D', '6070727198404A40', '60A008C4D5A3B8F2', '6184849918C8A94A', '619D3C459732BFDC', '633CB6082D5FF13A', '6363273F6349DBE6', '64597B30278462F5', '64E669C58B152047', '66132FEF55AE5854', '669F197DC7EB4FD9', '66BBDB594A250B3B', '66C2398A8C8A8335', '66FC298441D50783', '67498C0E4D4F1697', '6749FD012B7C93FD', '681FEAB4C02403C9', '68775744A4EB8649', '692705CAB94C3834', '693A3CB45929D1B2', '69924C768EE3374C', '69E475C5708ABE56', '6A01DD5AF15B5187', '6A07E9483E1D2638', '6A581C74EF4D5F44', '6A698019769B0F16', '6A7BCBD9BB3A3097', '6A7F8D10E390B4F5', '6ADFC435DBE4D6EF', '6B03981851818A0F', '6B4FA9A39FC379E8', '6CEE9CA228CCBA3C', '6D88BE6410DBB984', '6D98E5AEC0DCFF0B', '6DB2AB2869590A63', '6DECB18EA848DDDA', '6E5952194C02A488', '6E75BB608BDBFD49', '6EA1A93DD87FC2B7', '6F5F0C6161DCC360', '6FC1860C26779058', '701B5B6292603C20', '702566DE3898CB6C', '70BF82527E6ED9C3', '71DC282C52CFC244', '72742C5F312A1FEC', '729F6099B1B31935', '7342090A9E50B657', '74285DE881A72F4C', '743EFA16F928E18B', '747A57ED229B9235', '74DA40C35F685C4E', '74EEC94A70685C1E', '75AC3DC6E14554D0', '75C1434A74B03290', '75CBBBB56CFC020E', '76174428B80A08A0', '762F7B71CE6B0BF0', '768C51C0B882AB6D', '770B5C9E0E546E44', '77E438939CEADFAB', '782A69A1E16ACB23', '788724F2A10C59FE', '78CE39387962B153', '7963560AC5D4166E', '79656F14758D76BA', '798E2463C84ECAD7', '7A504D510F9703FB', '7A6E345A0058C71B', '7B0A1B548046E35B', '7B8F59B55EB43A91', '7BC59B90C9447C42', '7C67ABA421AF74D5', '7C9CA3F88B8755F1', '7CAC1DCC440BB214', '7CBE6C0B1530D0C8', '7CEFBD3C6ABEF91A', '7D346928024E5D79', '7D4C6A8C20B204F1', '7DD97D3E8E2C5E85', '7E26530508267F69', '7F2C298110AA73B4', '7FF809153882541B', '80055F949A01DDAD', '8013F2257A25F9BD', '80256304BFCD58C8', '802AE3764E188FC6', '8039D09D1E1B382C', '80D722A2C625E8B9', '814DF3DA9DD71B8A', '81548C930A397EBC', '818AEF8DE8B21EA7', '819449F677945F98', '82087A12CCB2F2AD', '82240827D9FAFDBC', '823BE193CDC32ADF', '82952C2B5CFAD42B', '82AE150FE103247A', '82E92E637C47885A', '83326C8A4BDB6BE1', '83E9CFC64D61108E', '846ABAF7694AA1A3', '84EB34779A251529', '851F6015525A2374', '856CDD7A9ECC45B2', '85D9DB7B0BC24D2A', '8683850A677DCC7A', '87B112DF92E5A3BA', '87FFD32D80DA1933', '894400D17C4E34C2', '8A2FACABB19EDC70', '8A401E7C2E0DAE58', '8A494F7F70AA2A2A', '8AA7482655CA47FC', '8AAB656D95891646', '8AE7EBC9CA45EEB7', '8B2134D31A514E4E', '8B44602F93F6CFB7', '8BF5CA0B2A04EB04', '8C0C9BBA51E96C48', '8C5E672BF19D5DEE', '8C66932374EF8009', '8C711C385D747C84', '8C88FA30D91583F7', '8CBBDC8958E20A2F', '8CE0BDF8F59A752E', '8D46D7EB5112DC9D', '8D4D2425F80F6A74', '8DC451FDE0E14B8C', '8DD7CC271D8CD039', '8DE1FF5E69F5124B', '8E0712757CE41DE4', '8E52B17EF9DCA68B', '8EE3018FC4D96F3F', '8F4577B4DEEAD5E1', '8F5B3D96E7D43A02', '8F98834C531143DA', '8FB9E95B030D25C5', '8FBAEB717ABA3930', '8FFA1D6F89AAEA40', '901C0498FF3E6796', '90BD4071AB4D6A78', '910E839ABF698914', '921FFC6F0F506A82', '92701F9DF59DC422', '928364C4C9F13FA8', '92CFDFB0A842D200', '93433E129ACF7E2D', '934A57222FCEEB83', '953EF42BFB998A53', '95401934D6D6D4FC', '954C980B4A9559FF', '95665DC1584AD532', '958426148AFA05C5', '95A9423B37553B4B', '966E398BAC214D29', '96749D41A2185B46', '9696D5EA6E1335C8', '9705F9A636A8C2C9', '971CD60FDB1BF254', '972CBA9F98130D80', '9745CE1D5B111CB1', '983051C6A972439D', '983D91483B077D54', '98949A08011E22F6', '992B70348B5E398C', '99A730464C4BB13C', '9A69D975A4ABE47C', '9A6CE800C60794BD', '9B234D350E686A67', '9B62277AA9225059', '9B73C945ABD15531', '9B8BFBACE614F651', '9BD9E79856D54059', '9C99E4676D6A8B44', '9D1710D562537AA8', '9D5D1CAC16E9262D', '9E537FA089BAECFE', '9EBDA30C87A7B9B3', '9F15AB8A7F968248', '9F17DAA9F8821330', '9F27772DC3EACA65', '9F4F58990EAB3DFE', '9FBB46EF36C35FD3', 'A070519278350129', 'A0ACD5C989AFEF92', 'A0C509818D9A54CA', 'A0ECB4825C9B8289', 'A0FCDFB113ABF06F', 'A121E8F760F7D482', 'A1BCBE234E188E49', 'A2505D1062E77B85', 'A2614E5A9AC15FC8', 'A27693967E58ADD5', 'A372F552AC2066B7', 'A3EDC1FFCB20AEAE', 'A3FE6D821476E810', 'A4E5755E0F7B094D', 'A517E4DD3ECFB0D9', 'A5ACC90F4E6C9D47', 'A62BEB5AF596DB62', 'A71444B5BEB5CCC4', 'A720D7FFE2FC85BC', 'A7957CDA89BDE94F', 'A7E7871D2D2637C4', 'A81BA2A7B5FD1862', 'A8533224E9384B16', 'A8738C98EA4BB1A7', 'A8C25A605F613665', 'A8C5F4D253DCEF2F', 'A907C114B5846EF0', 'A926366D229CB25C', 'A944D2BCDFD50619', 'AA5A1803D3FA76B4', 'AB0D9A9D709F476A', 'AB30C03FCEC72466', 'AB5EF9587051A99D', 'ABB61823786179CC', 'ABC0D4B6F3DE650C', 'ABDE941B896D8119', 'ACDCFF5D6EEDBEAC', 'ACDFC703CACAC820', 'AEB9B39AE54D3598', 'AF987F436A7EBD21', 'AF9FD44E4FBE7A44', 'AFF3D30CC385F0E9', 'AFFC4117AC279D49', 'B02C18F3C75E4F37', 'B05232A00CE7DA9B', 'B15D97BB5A951978', 'B1BEB31C7C6F966B', 'B2795EB0A968DFB5', 'B28C8D3E19E8A789', 'B2BB6F16652B21BF', 'B2D168BAC367B509', 'B3BD1DE2ACEEBC87', 'B4BA80B4BD6D550C', 'B4BAC8172F358933', 'B5006488F35ACDDB', 'B50ECB33FCECA482', 'B51F94359F04F11C', 'B54F288E9548E94E', 'B593E1E1E045C398', 'B5DC591BA6B42056', 'B5E7E222F1A5D7A6', 'B619C432EDEC453F', 'B6A3FD2473DDF30F', 'B6CFC956973D56CD', 'B7DBE079E5AB148E', 'B84DFF47E82A9FDF', 'B88CD7F1ECE6FAB6', 'B8F008F4479822AC', 'B956889B759F3E32', 'BA98FD8F84C838C7', 'BABB70C636BA523A', 'BAEFF105F449EDED', 'BAF6250F863EFA7E', 'BB7E54F566F3591B', 'BCF874A80C49209D', 'BD5FC2C0F26FC36D', 'BD7CF9238F6A1A07', 'BE161380813AA0F3', 'BE3C02D74A664180', 'BE8EF48E1C8A03BB', 'BEF16764A13AEC34', 'BF1EB8E6FA3F19B8', 'C04E36E535A6F740', 'C1EF154BABDC9A09', 'C21237DC47AE00C5', 'C2398EA0078EA63A', 'C2A2B2ADB29B8D74', 'C2F23593792D11D1', 'C3239111D782A4D5', 'C36784B608C9A693', 'C3C88EC13873148A', 'C472EA28E25BE6D7', 'C51F70F9545C0D4F', 'C55976AC94BFB22C', 'C5B80CE5D26813CF', 'C5C355D72A66F9FB', 'C632F81395C23676', 'C6647391F831C413', 'C68A124C91E59F64', 'C6FF68D218AAC396', 'C70D7F9553FF0C48', 'C72B62A241061928', 'C7750723906C925A', 'C77D7125A7F73F33', 'C799CEF5E1C8D488', 'C845E42170625895', 'C865891D80127AB8', 'C89E24F4C723CEF4', 'C8BDDF5639850737', 'C91282DA643F3ECA', 'C96DBA8C2C03EBC1', 'C99F4A393066D056', 'C9F4E51F24E6BCE1', 'CA05B60C5BF619D0', 'CA9D3DEA00836355', 'CB0255E90DB0FED4', 'CB5F0081A83B7397', 'CBC034EE5345D8BD', 'CC6C607F45AF7226', 'CC9D5FE5F9DC8D01', 'CDBDCFC2BE3CF712', 'CE3108804954FEC2', 'CE48F47093B03425', 'D0533ECF2A67B35C', 'D070FD0BFE78DFA4', 'D0D1426BAFDD1FE5', 'D0D8873714077818', 'D0ECAB9DA4FE2E00', 'D12A5C7D0C2AFCE6', 'D150AD7D66BFE61A', 'D1BB2F57BCD02BAE', 'D1D7C0CCE823D871', 'D2A9C2B69B9F4574', 'D2D8E67B7D0DE2D2', 'D2E647207224B3CB', 'D2F6FB926A64D196', 'D39EEEC785C51298', 'D40A89E14F1C10BD', 'D493E891D8F64BBC', 'D50BB2F89A4A8A71', 'D5DD44281759F7C8', 'D5DFEBAC8C8543A7', 'D63375DEBF111D81', 'D70EF43499FEBA88', 'D7663B1CF2680ECB', 'D78FF240E6D237B6', 'D7933558E33BF7D5', 'D857FFFE7A6AC9A4', 'D902807C00F2AF2D', 'DABC14FDDFADD29E', 'DAEE6D470B64E11B', 'DBCA5313FEE8A3B9', 'DBF9BDC859BC4459', 'DC24142B23D9FEDD', 'DC5EBC31C22D87B8', 'DD155919BA4AC26E', 'DD9369338455727C', 'DDF23E1CEC60F6C5', 'DE36357248F07700', 'DE7E58B105D38F62', 'DE98BA6D3814A6D4', 'DF9C2411ADA62C66', 'E09EE3450F1FDDEB', 'E12E58566E7B52DC', 'E1489CCA162BC2E6', 'E26CE7138789A7AF', 'E26F13A65CEAC6EA', 'E2E336528AB6202A', 'E2F981AE3D6A2CE8', 'E32D4C0315D6BFBE', 'E34104028365019C', 'E341ABC3EDB0AECD', 'E3481D64A20DA394', 'E3780F6861A69868', 'E3AF95C5CDBA8593', 'E3FE22D7C70B05E6', 'E40FA02A8A53F693', 'E4129EBDCA809BE8', 'E48564406C361C95', 'E53ECFC837EB8022', 'E594B711ADD81DD8', 'E5C1C86BCE045D73', 'E62E2664B4743026', 'E6911DABCAC3EFF2', 'E6AAFF21D051CC65', 'E6FE73B8B903B31B', 'E7692088C79C239E', 'E785E40D0C268712', 'E7F87F9FBA3269B1', 'E85E1F634E368569', 'E88883CDEF8783D6', 'E8D19649BE5927B3', 'E8D3E6385C8B875A', 'E93B6FE3C56ABA69', 'E9953B60E82F35E8', 'EA59CBC69BD2351F', 'EA62EC33715AE967', 'EA6EA431FF84563B', 'EAE7A5BE3C3D93B8', 'EBBD0BC203F21830', 'EBE2130151519EF6', 'EC9E9E250559033B', 'ECAE04CCCF52B16D', 'ECB7F5A1A2E1D00F', 'ED0814ECF62C56CA', 'ED11170D5BB9D744', 'ED47C7C2CD16539E', 'ED7297A4D3BFA953', 'ED9745F4A0B682B8', 'EDC7E08BC7DA267D', 'EDDE06B4239A55EE', 'EE9E527A761299B2', 'EF3118771FB74F92', 'EF449601C94D2496', 'F03F667009F16326', 'F04D6A0624CD2D1E', 'F0B92123E5142CBB', 'F0BD18FF153ED188', 'F10551276E3FDD53', 'F1B93BAE75EE0FB2', 'F24DA4C9406263D2', 'F26444BA1B35C213', 'F2CFD8007F2530E3', 'F34FBD4CB9207474', 'F36F4FC5A884C3DA', 'F37346E94569EA9E', 'F3A05C8D912975AB', 'F3C90449CB042238', 'F3E821DFEDE6BF03', 'F4124F43827BAA36', 'F52738C4461D426E', 'F540E917082B3F70', 'F561D76FF3C35292', 'F571D96ED3330551', 'F6785D85CBDA2BA6', 'F6C716472A556D8A', 'F71DD44DAB783CEB', 'F74B5B5FBD569D94', 'F76A4CA089865328', 'F7B9D8D3C0FC9881', 'F7BAFF2E0A04F11B', 'F8443EA4228113A4', 'F8564F547AFFC0B9', 'F89DE21913485212', 'F9945FBF588762BE', 'F9A5381028C80728', 'F9B6AD71F73676ED', 'FA8706D1E274805E', 'FABB6D49B11E673F', 'FB67C0C00EAF6628', 'FB75493CD55739BA', 'FB9FFB4BA8695C78', 'FC2DA0D7EF375B27', 'FC37CBE8211E02A8', 'FC67251B307846CB', 'FD11BEB17BD1E5F1', 'FD348F3ADE080914', 'FD83244D94C592DB', 'FDF035F9D4A1EB66', 'FE16209BD3C61223', 'FE578FBF32343FC3', 'FE9B25977A8537C2', 'FEE93E9CF4315356']
df_ab_group.query('user_id=="0240DB73D9B6F136"')
| user_id | first_date | region | device | event_dt | event_name | details | date | group | ab_test | day_count | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 15806 | 0240DB73D9B6F136 | 2020-12-10 | EU | PC | 2020-12-10 07:23:24 | login | NaN | 2020-12-10 | B | recommender_system_test | 0.0 |
| 15807 | 0240DB73D9B6F136 | 2020-12-10 | EU | PC | 2020-12-12 07:39:10 | login | NaN | 2020-12-12 | B | recommender_system_test | 2.0 |
len(single_event_users)
695
В наших данных 695 пользователей не совершивших ни одного события, кроме login. Убираю их из анализа
df_ab_group = df_ab_group[~df_ab_group['user_id'].isin(single_event_users)]
Фильтрую датасет по количеству дней, не более 14
df_ab_group = df_ab_group.query('day_count<=13')
df_ab_group = df_ab_group.reset_index()
df_ab_group.groupby('group')['user_id'].agg('nunique')
group A 2120 B 666 Name: user_id, dtype: int64
После фильтрации по количеству дней, количество пользователей в группе А=2120 а в группе В=666
Cтроим гистограмма х возраст и у количество событий двойная для обоих групп
t_sleep_a = df_ab_group.query('group=="A"')
t_fresh_b = df_ab_group.query('group=="B"')
plt.subplots(figsize=(8, 4), dpi=100)
plt.hist(t_sleep_a['day_count'] , alpha=0.5, label="группа А")
plt.hist(t_fresh_b['day_count'],color='red', alpha=0.5, label="группа В")
plt.axvline(t_sleep_a['day_count'].mean(), color='blue', linestyle='dashed', linewidth=2, label='Mean')
plt.axvline(t_fresh_b['day_count'].mean(), color='red', linestyle='dashed', linewidth=2, label='Mean')
plt.legend(loc='upper right')
plt.title('Гистограмма распределения по количеству дней по группам')
plt.xlabel('Количество дней')
plt.ylabel('количество пользователей')
plt.show()
На гистограмме видно что среднее количество дней на совершение событий в обоих греппах примерно 3 дня. Но большинство событий происходит в первый день, например у группы В это 2000 пользоателей а у группы А 8000 пользователей
df_ab_mean_a = df_ab_group.query('group=="A"')
df_ab_mean_b = df_ab_group.query('group=="B"')
t_mean_a = df_ab_mean_a.groupby('user_id')['event_name'].count()
t_mean_b = df_ab_mean_b.groupby('user_id')['event_name'].count()
print('Cреднее количество событий на пользователя, группа А:',round(t_mean_a.mean(),2))
print('Cреднее количество событий на пользователя, группа В:',round(t_mean_b.mean(),2))
Cреднее количество событий на пользователя, группа А: 7.69 Cреднее количество событий на пользователя, группа В: 6.25
В среднем в группах пользователи совершали по 7,7 событый в группе А и по 6,25 в В
Сторою график набора пользователей
users_by_date_a = t_sleep_a.groupby('first_date')['user_id'].nunique()
users_by_date_b = t_fresh_b.groupby('first_date')['user_id'].nunique()
plt.subplots(figsize=(8, 4), dpi=100)
plt.bar(users_by_date_a.index, users_by_date_a.values , alpha=0.5, label="группа А")
plt.bar(users_by_date_b.index, users_by_date_b.values,color='red', alpha=0.5, label="группа В")
plt.legend(loc='upper right')
plt.title('Динамика набора пользователей по дням регистрации и группам')
plt.xlabel('Дата регистрации')
plt.ylabel('Количество пользователей')
plt.xticks(rotation=45)
plt.show()
По гистограмме видно, что в первый день начала теста в группу В попало больше пользователей чем в группу А, через неделю был резкие скачок притока пользователей, особенно в группу А. Так как у нас пользователей в группе А гораздо больше чем в тестовой, это хорошо видно на графике. Распределение пользователей из нашего региона ранее определил ранее что 17 процентов учавствуют на нашем тесте
Посмотрим распределение по дням недели
df_ab_group['day_of_week'] = df_ab_group['first_date'].dt.day_name()
df_ab_group_a = df_ab_group.query('group=="A"')
df_ab_group_b = df_ab_group.query('group=="B"')
users_by_day_a = df_ab_group_a.groupby('day_of_week')['user_id'].nunique()
users_by_day_b = df_ab_group_b.groupby('day_of_week')['user_id'].nunique()
plt.subplots(figsize=(8, 4), dpi=100)
plt.bar(users_by_day_a.index, users_by_day_a.values , alpha=0.5, label="группа А")
plt.bar(users_by_day_b.index, users_by_day_b.values,color='red', alpha=0.5, label="группа В")
plt.legend(loc='upper right')
plt.title('Динамика набора пользователей по дням недели и группам')
plt.xlabel('День недели')
plt.ylabel('Количество пользователей')
plt.xticks(rotation=45)
plt.show()
Чаще всего пользователи попадали в тест по понедельникам, что в группе А то и В. В остальные дни недели относительно равномерно
users_by_day_a = df_ab_group_a.groupby('device')['user_id'].nunique()
users_by_day_b = df_ab_group_b.groupby('device')['user_id'].nunique()
plt.subplots(figsize=(8, 4), dpi=100)
plt.bar(users_by_day_a.index, users_by_day_a.values , alpha=0.5, label="группа А")
plt.bar(users_by_day_b.index, users_by_day_b.values,color='red', alpha=0.5, label="группа В")
plt.legend(loc='upper right')
plt.title('Распределение пользователей устройствам и группам')
plt.xlabel('Устройство')
plt.ylabel('Количество пользователей')
plt.xticks(rotation=45)
plt.show()
Чаще всего пользователи в обоих группах заходили на платформу с Android и PC
Среднее количество событий на пользователя в группе
events_per_user_a = df_ab_group_a.groupby('user_id')['event_name'].count()
events_per_user_b = df_ab_group_b.groupby('user_id')['event_name'].count()
print(events_per_user_a.mean())
print(events_per_user_b.mean())
7.694811320754717 6.253753753753754
В среднем в группе А совершали 7,7 событий а в группе В 6,25 события
Смотрим даты совершения событий участникам теста
df_ab_group = df_ab_group.drop('index',axis=1)
df_ab_g = df_ab_group.pivot_table(index='date', columns=['event_name','group'], values='user_id', aggfunc='nunique')
# построение столбчатой диаграммы
fig, ax = plt.subplots(figsize=(25, 8))
df_ab_g.plot(kind='bar', ax=ax)
ax.set_xlabel('Дата')
ax.set_ylabel('Количество пользователей')
ax.set_title('Количество уникальных пользователей по событиям и группам')
plt.show()
Всплеск активности пользователей пришелся на 21/12. И это всплеск не связан ни с какими маркетинговыми событиями. Только одно мероприятие проходило в период и регионе проведения нашего теста. С 25/12 по 03/01 мероприятие "Christmas&New Year Promo"
users_day_a = df_ab_group_a.groupby('date')['event_name'].count()
users_day_b = df_ab_group_b.groupby('date')['event_name'].count()
plt.subplots(figsize=(8, 4), dpi=100)
plt.bar(users_day_a.index, users_day_a.values , alpha=0.5, label="группа А")
plt.bar(users_day_b.index, users_day_b.values,color='red', alpha=0.5, label="группа В")
plt.legend(loc='upper right')
plt.title('Общий график всех событий')
plt.xlabel('Дата')
plt.ylabel('Количество событий')
plt.xticks(rotation=45)
plt.show()
Расмотрим отдельно по группам
df_pivot_a = df_ab_group_a.pivot_table(index='date', columns='event_name', values='user_id', aggfunc='nunique')
fig = px.bar(df_pivot_a, barmode='group')
fig.update_layout(title={
'text': "Количество уникальных пользователей по датам и событиям в группе А",
'y':0.95,
'x':0.5,
'xanchor': 'center',
'yanchor': 'top'}, xaxis_title='Дата', yaxis_title='Количество пользователей')
fig.show()
df_pivot_b = df_ab_group_b.pivot_table(index='date', columns='event_name', values='user_id', aggfunc='nunique')
fig = px.bar(df_pivot_b, barmode='group')
fig.update_layout(title={
'text': "Количество уникальных пользователей по датам и событиям в группе В",
'y':0.95,
'x':0.5,
'xanchor': 'center',
'yanchor': 'top'}, xaxis_title='Дата', yaxis_title='Количество пользователей')
fig.show()
В группе А резкий всплеск активности наблюдается с 14/12 и подьем продолжается до 21/12, после чего идет резкий спад, В группе В с превого дня относительная стабильность по событиям, но спад также наблюдается с 21/12. По этим данным можно сказать что общее поведение в обоих группах одинаковое Интересно что же так повлияло на активность пользователей. Стоит обратить внимание что есть существенная разница в показателях на оси У наших графиков, Количество пользователей в обоих группах не одинаковое
Ранее мы уже убрали из анализа пользователей которые не совершили ни одного действия и не прожили 14 дней
df_ab_d = df_ab_group.pivot_table(index='day_count', columns=['event_name','group'], values='user_id', aggfunc='nunique')
# построение столбчатой диаграммы
fig, ax = plt.subplots(figsize=(20, 8))
df_ab_d.plot(kind='bar', ax=ax)
ax.set_xlabel('День совершения действия с даты регистрвции')
ax.set_ylabel('Количество пользователей')
ax.set_title('Количество уникальных пользователей по Дням совершения события')
plt.show()
В обоих группах события весь спектр событий совершается в первый день. Посмотрим по группам отдельно
df_pivot_aa = df_ab_group_a.pivot_table(index='day_count', columns='event_name', values='user_id', aggfunc='nunique')
fig = px.bar(df_pivot_aa, barmode='group')
fig.update_layout(title={
'text': "Количество уникальных пользователей по Cобытиям в группе А",
'y':0.95,
'x':0.5,
'xanchor': 'center',
'yanchor': 'top'}, xaxis_title='День после регистрации', yaxis_title='Количество пользователей')
fig.show()
df_pivot_ab = df_ab_group_b.pivot_table(index='day_count', columns='event_name', values='user_id', aggfunc='nunique')
fig = px.bar(df_pivot_ab, barmode='group')
fig.update_layout(title={
'text': "Количество уникальных пользователей по Cобытиям в группе В",
'y':0.95,
'x':0.5,
'xanchor': 'center',
'yanchor': 'top'}, xaxis_title='День после регистрации', yaxis_title='Количество пользователей')
fig.show()
В обоих группах, большинство пользователей совершают все события в первый день после регистрации, И чаще всего минуют событие корзина, и сразу переходят на событие Оплата. Можно сказать что поведение в группах довольно одинаковое
Убедитесь, что время проведения теста не совпадает с маркетинговыми и другими активностями.
# выбираем период времени, в котором проводился тест
test_start = df_ab_group['date'].min()
test_end = df_ab_group['date'].max()
# выбираем период времени, в котором проводились маркетинговые события
marketing_start = df_ab['start_dt'].min()
marketing_end = df_ab['finish_dt'].max()
# проверяем пересечение периодов времени
if (test_start >= marketing_start and test_start <= marketing_end) or (test_end >= marketing_start and test_end <= marketing_end):
print('Внимание! Время проведения теста пересекается с маркетинговыми активностями.')
else:
print('Время проведения теста не пересекается с маркетинговыми активностями.')
# выводим список событий, которые пересекаются с маркетинговыми активностями
intersect_events = df_ab_group[df_ab_group['event_dt'].between(marketing_start, marketing_end)]['event_name'].unique()
if intersect_events.size > 0:
print('Следующие события пересекаются со временем проведения маркетинговых активностей:')
print(intersect_events)
else:
print('Нет событий, пересекающихся со временем проведения маркетинговых активностей.')
Внимание! Время проведения теста пересекается с маркетинговыми активностями. Следующие события пересекаются со временем проведения маркетинговых активностей: ['product_page' 'login' 'purchase' 'product_cart']
intersect_marketing = df_ab[(df_ab['start_dt'] <= test_end) & (df_ab['finish_dt'] >= test_start)]['event_name'].unique()
if intersect_marketing.size > 0:
print('Тест пересекается с маркетинговыми активностями:')
for marketing in intersect_marketing:
marketing_dates = df_ab[df_ab['event_name'] == marketing][['start_dt', 'finish_dt']].values
print(f'Маркетинговое событие "{marketing}" проводилось в период с {marketing_dates[0][0]} по {marketing_dates[0][1]}.')
else:
print('Тест не пересекается с маркетинговыми активностями.')
Тест пересекается с маркетинговыми активностями: Маркетинговое событие "Christmas&New Year Promo" проводилось в период с 2020-12-25T00:00:00.000000000 по 2021-01-03T00:00:00.000000000.
В очередной раз подтвердили данные что толоко одно маркетинговое событе происходило в дни проведения теста, это Маркетинговое событие "Christmas&New Year Promo" проводилось в период с 2020-12-25 по 2021-01-03. И оно ни как не могло повлиять на активность пользователей в обоих группах
Продуктовая воронка: постройте простые продуктовые воронки для двух групп теста с учетом логической последовательности совершения событий; изучите изменение конверсии в продуктовой воронке тестовой группы, по сравнению с контрольной: наблюдается ли ожидаемый эффект увеличения конверсии в группе В, относительно конверсии в группе А? Сделайте общий вывод об изменении пользовательской активности в тестовой группе, по сравнению с контрольной.
user_uniq_count_a = df_ab_group_a['user_id'].nunique()
user_uniq_count_b = df_ab_group_b['user_id'].nunique()
tt = df_ab_group.query('event_name=="login"')
tt['user_id'].nunique()
2785
users_without_login = df_ab_group[~df_ab_group['user_id'].isin(df_ab_group[df_ab_group['event_name'] == 'login']['user_id'])]['user_id'].unique()
# выведем список таких пользователей
print('Пользователи без события login:')
print(users_without_login)
Пользователи без события login: ['5FF8B6AB257B404F']
df_ab_group.query('user_id=="5FF8B6AB257B404F"')
| user_id | first_date | region | device | event_dt | event_name | details | date | group | ab_test | day_count | day_of_week | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1573 | 5FF8B6AB257B404F | 2020-12-07 | EU | Android | 2020-12-07 04:17:47 | purchase | 4.99 | 2020-12-07 | B | recommender_system_test | 0.0 | Monday |
Случайно обнаружился пользователь без Логина даже. Странно. Но в целом понятно,что по событию логин строить воронку не стоит, конверсия 100%
ttpp_a = df_ab_group.query('event_name=="product_page" and group=="A"')
user_count_pp_a = round(ttpp_a['user_id'].nunique()/user_uniq_count_a*100,2)
ttpp_b = df_ab_group.query('event_name=="product_page" and group=="B"')
user_count_pp_b = round(ttpp_b['user_id'].nunique()/user_uniq_count_b*100,2)
ttpc_a = df_ab_group.query('event_name=="product_cart" and group=="A"')
user_count_pc_a = round(ttpc_a['user_id'].nunique()/user_uniq_count_a*100,2)
ttpc_b = df_ab_group.query('event_name=="product_cart" and group=="B"')
user_count_pc_b = round(ttpc_b['user_id'].nunique()/user_uniq_count_b*100,2)
ttpu_a = df_ab_group.query('event_name=="purchase" and group=="A"')
user_count_pu_a = round(ttpu_a['user_id'].nunique()/user_uniq_count_a*100,2)
ttpu_b = df_ab_group.query('event_name=="purchase" and group=="B"')
user_count_pu_b = round(ttpu_b['user_id'].nunique()/user_uniq_count_b*100,2)
stages = ["product_page", "product_cart", "purchase"]
df_mtl = pd.DataFrame(dict(number=[user_count_pp_a, user_count_pc_a, user_count_pu_a], stage=stages))
df_mtl['group'] = 'A'
df_tor = pd.DataFrame(dict(number=[user_count_pp_b, user_count_pc_b, user_count_pu_b], stage=stages))
df_tor['group'] = 'B'
df = pd.concat([df_mtl, df_tor], axis=0)
fig = px.funnel(df, x='number', y='stage', color='group')
fig.show()
На графике конверсии видно что по группам и на каждом шаге они существенных различий нет, например конверсия у первого события "Переход на страницу с товаром" у группы А 80% а у группы В только 74%. а до оплаты у группы А доходит 39% у В 37. Судя по этой воронке можно уверенно сказать что изменения проводимые с тестовой (В) группой не принесли успешного результата
ttlo_a = df_ab_group.query('event_name=="login" and group=="A"')
user_count_lo_a = round(ttlo_a['user_id'].nunique()/user_uniq_count_a*100,2)
ttlo_b = df_ab_group.query('event_name=="login" and group=="B"')
user_count_lo_b = round(ttlo_b['user_id'].nunique()/user_uniq_count_b*100,2)
ttpp_a = df_ab_group.query('event_name=="product_page" and group=="A"')
user_count_pp_a = round(ttpp_a['user_id'].nunique()/user_uniq_count_a*100,2)
ttpp_b = df_ab_group.query('event_name=="product_page" and group=="B"')
user_count_pp_b = round(ttpp_b['user_id'].nunique()/user_uniq_count_b*100,2)
ttpc_a = df_ab_group.query('event_name=="product_cart" and group=="A"')
user_count_pc_a = round(ttpc_a['user_id'].nunique()/user_uniq_count_a*100,2)
ttpc_b = df_ab_group.query('event_name=="product_cart" and group=="B"')
user_count_pc_b = round(ttpc_b['user_id'].nunique()/user_uniq_count_b*100,2)
ttpu_a = df_ab_group.query('event_name=="purchase" and group=="A"')
user_count_pu_a = round(ttpu_a['user_id'].nunique()/user_uniq_count_a*100,2)
ttpu_b = df_ab_group.query('event_name=="purchase" and group=="B"')
user_count_pu_b = round(ttpu_b['user_id'].nunique()/user_uniq_count_b*100,2)
stages = ["login","product_page", "product_cart", "purchase"]
df_mtl = pd.DataFrame(dict(number=[user_count_lo_a, user_count_pp_a, user_count_pc_a, user_count_pu_a], stage=stages))
df_mtl['group'] = 'A'
df_tor = pd.DataFrame(dict(number=[user_count_lo_b, user_count_pp_b, user_count_pc_b, user_count_pu_b], stage=stages))
df_tor['group'] = 'B'
df = pd.concat([df_mtl, df_tor], axis=0)
fig = px.funnel(df, x='number', y='stage', color='group')
fig.show()
user_c_pp_a = ttpp_a['user_id'].nunique()
user_c_pp_b = ttpp_b['user_id'].nunique()
user_c_pc_a = ttpc_a['user_id'].nunique()
user_c_pc_b = ttpc_b['user_id'].nunique()
user_c_pu_a = ttpu_a['user_id'].nunique()
user_c_pu_b = ttpu_b['user_id'].nunique()
Формулирую гипотезы, по событиям:"product_page", "product_cart", "purchase" между группами А и В:
Н0: В сравниваемых группах нет статистически значимой разницы
Н1: В сравниваемых группах есть статистически значимая разница
alpha = 0.05
successes = [user_c_pp_a, user_c_pp_b]
trials = [user_uniq_count_a, user_uniq_count_b]
stat, p_value = proportions_ztest(successes, trials)
print('\nШаг 1: MainScreenAppear')
print('p-значение:', p_value)
if p_value < alpha:
print('Отвергаем нулевую гипотезу: различие конверсий на шаге 1 статистически значимо')
else:
print('Не получилось отвергнуть нулевую гипотезу: различие конверсий на шаге 1 не статистически значимо')
Шаг 1: MainScreenAppear p-значение: 0.0029370596204435742 Отвергаем нулевую гипотезу: различие конверсий на шаге 1 статистически значимо
def check_conversion_significance(user_uniq_count_a, user_uniq_count_b, user_c_pp_a, user_c_pp_b, user_c_pc_a, user_c_pc_b, user_c_pu_a ,user_c_pu_b):
alpha = 0.14
successes = [user_c_pp_a, user_c_pp_b]
trials = [user_uniq_count_a, user_uniq_count_b]
stat, p_value = proportions_ztest(successes, trials)
print('\nШаг 1: product_page')
print('p-значение:', p_value)
if p_value < alpha:
print('Отвергаем нулевую гипотезу: различие конверсий на шаге 1 статистически значимо')
else:
print('Не получилось отвергнуть нулевую гипотезу: различие конверсий на шаге 1 не статистически значимо')
successes = [user_c_pc_a, user_c_pc_b]
trials = [user_uniq_count_a, user_uniq_count_b]
stat, p_value = proportions_ztest(successes, trials)
print('\nШаг 2: product_cart')
print('p-значение:', p_value)
if p_value < alpha:
print('Отвергаем нулевую гипотезу: различие конверсий на шаге 2 статистически значимо')
else:
print('Не получилось отвергнуть нулевую гипотезу: различие конверсий на шаге 2 не статистически значимо')
successes = [user_c_pu_a, user_c_pu_b]
trials = [user_uniq_count_a, user_uniq_count_b]
stat, p_value = proportions_ztest(successes, trials)
print('\nШаг 3: purchase')
print('p-значение:', p_value)
if p_value < alpha:
print('Отвергаем нулевую гипотезу: различие конверсий на шаге 3 статистически значимо')
else:
print('Не получилось отвергнуть нулевую гипотезу: различие конверсий на шаге 3 не статистически значимо')
check_conversion_significance(user_uniq_count_a, user_uniq_count_b, user_c_pp_a, user_c_pp_b, user_c_pc_a, user_c_pc_b, user_c_pu_a ,user_c_pu_b)
Шаг 1: product_page p-значение: 0.0029370596204435742 Отвергаем нулевую гипотезу: различие конверсий на шаге 1 статистически значимо Шаг 2: product_cart p-значение: 0.9070540547116017 Не получилось отвергнуть нулевую гипотезу: различие конверсий на шаге 2 не статистически значимо Шаг 3: purchase p-значение: 0.37888740748690086 Не получилось отвергнуть нулевую гипотезу: различие конверсий на шаге 3 не статистически значимо
При проверке статистически значимой разницы выборки, только на событии "product_page" не подтвердил гипотезу что доли в выборке равны, на остальных этапах "Корзина" и "Покупка", доли в выборка статистических различий не имеют. Можно сказать, что изменения, внесенные на этапе представления товара, оказали влияние на поведение пользователей, в то время как изменения на этапах корзины и покупки - нет. В целом тест можно считать не удачным, различия только на одном событии. А задача теста увеличить конверсию на всех не менее 5%. По конверсии мы видели что в абсолютных показателях результат также не достигнут
Во время выполнения проекта были проведены следеющие этапы работ:
Прведена проверка на соответствие Техническому заданию. Сделаны следующие заключения: